package playGround.square;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import playGround.border.Border;
import playGround.border.Door;
import playGround.border.Wall;
import playGround.dungeon.CompositeDungeon;
import playGround.dungeon.Direction;
import playGround.dungeon.Level;
import playGround.dungeon.Position;
import exceptions.IllegalBorderException;



public class SquareTest {
	
	private Square square;
	private Square square1;
	private Square square2;
	private Square square3;
	private Border wall1;
	private Level<Square> dungeon;
	private Position maxPosition;
	private Map<Direction, Border> borders;
	@Before
	public void setUp() throws Exception {
		maxPosition=new Position(100,100,0);
		dungeon= new Level<Square>(maxPosition);
		square = new OrdinarySquare();
		square1 = new OrdinarySquare();
		wall1=new Wall(false);
		Border wall2 = new Wall(false);
		borders = new HashMap<Direction, Border>();
		borders.put(Direction.FLOOR,wall1);
		borders.put(Direction.EAST, wall2);
		square2 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders);
		square3 = new OrdinarySquare(-50,new BigDecimal(10), Scale.CELCIUS, borders);
	}

	@After
	public void tearDown() throws Exception {
	}
	
	 @Test
	public void testColdDamageNull(){
		
		assertEquals(0,square.getColdDamage());
	}
	@Test
	public void addBorderTest(){
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.EAST, wall2);
		Square square4 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		assertEquals(wall2,square4.getBorder(Direction.EAST));
		assertEquals(true,square4.getBorder(Direction.EAST).containsSquare(square4));
	 }
	@Test(expected =IllegalBorderException.class)
	public void addDoorFloorTest(){
		Border door = new Door();
		square2.addBorder(Direction.FLOOR, door);
	}
	@Test
	public void testRemoveBorderByDirection() {
		square2.removeBorder(Direction.EAST);
		assertEquals(square2.getBorder(Direction.EAST),null);
	}
	@Test
	public void testRemoveBorderByBorder() {
		Border wall2 = new Wall(false);
		Border wall3 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.EAST, wall2);
		borders1.put(Direction.WEST, wall3);
		Square square4 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		wall2.removeSquare(square4);
		square4.removeBorder(wall2);
		assertEquals(false,square4.hasBorder(wall2));
		
	}
	@Test(expected=IllegalBorderException.class)
	public void testRemoveBorderByBorderFail() {
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.EAST, wall2);
		Square square4 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		wall2.removeSquare(square4);
		square4.removeBorder(wall2);
		assertEquals(false,square4.hasBorder(wall2));
		
	}
	@Test
	public void testHeatDamage() {
		int dmg = square2.getHeatDamage();
		dmg=0;
		assertNotSame(dmg,square2.getHeatDamage());
	}
	@Test
	public void testColdDamage() {
		int dmg = square3.getColdDamage();
		dmg=0;
		assertNotSame(dmg,square3.getColdDamage());
	}
	@Test
	public void testRustDamage() {
		square1.setHumidity(new BigDecimal(2));
		assertEquals(square2.getRustDamage(),0);
	}
	@Test
	public void testChangeScale(){
		square2.changeScale(Scale.KELVIN);
		assertEquals(573,square2.getTemperature());
	}
	@Test(expected = NullPointerException.class)
	public void testThrowsInConstructor(){
		new OrdinarySquare(6,new BigDecimal(1),null,borders);
	}
	@Test
	public void testThrowsInInhability(){
		square2.setMaxTemperature(Integer.MAX_VALUE);
		square2.setTemperature(Integer.MAX_VALUE);
		assertEquals(square2.getInhability(),Integer.MAX_VALUE);
	}
	@Test(expected = IllegalArgumentException.class)
	public void testThrowsInSetMinTemperature(){
		square2.setMinTemperature(400);
	}
	@Test
	public void testCalculateSlipperyMaterial(){
		Border wall3=new Wall(true);
		Map<Direction,Border> borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.WEST,wall3);
		Square square4 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		assertEquals(true,square4.getSlippery());
	}
	@Test
	public void testCalculateSlipperyAboveZero(){
		Border wall3=new Wall(false);
		Map<Direction,Border> borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.WEST,wall3);
		Square square4 = new OrdinarySquare(300,new BigDecimal(100), Scale.CELCIUS, borders1);
		assertEquals(true,square4.getSlippery());	
	}
	@Test
	public void testCalculateSlipperyUnderZero(){
		Border wall3=new Wall(false);
		Map<Direction,Border> borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.WEST,wall3);
		Square square4 = new OrdinarySquare(-50,new BigDecimal(20), Scale.CELCIUS, borders1);
		assertEquals(true,square4.getSlippery());
		
	}
	@Test
	public void testControlBorderDoorConflict(){
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.EAST, wall2);
		Square square4 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		Border door = new Door();
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.WEST, door);
		Square square5 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders2);
		square4.controlBorder(square5, Direction.EAST);
		assertEquals(wall2,square4.getBorder(Direction.EAST));
		assertEquals(wall2,square5.getBorder(Direction.WEST));
	}
	@Test
	public void testControlBorderNoOtherDoorConflict(){
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.EAST, wall2);
		Square square4 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		Border door = new Door();
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.EAST, door);
		Square square5 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders2);
		square4.controlBorder(square5, Direction.EAST);
		assertEquals(wall2,square4.getBorder(Direction.EAST));
		assertEquals(wall2,square5.getBorder(Direction.WEST));
	}
	@Test
	public void testControlBorderNoThisDoorConflict(){
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.NORTH, wall2);
		Square square4 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		Border door = new Door();
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.WEST, door);
		Square square5 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders2);
		square4.controlBorder(square5, Direction.EAST);
		assertEquals(wall2,square4.getBorder(Direction.NORTH));
		assertEquals(door,square4.getBorder(Direction.EAST));
		assertEquals(door,square5.getBorder(Direction.WEST));
	}
	@Test
	public void testControlBorderTwoDoorConflict(){
		Border door1 = new Door();
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.EAST, door1);
		Square square4 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		Border door = new Door();
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.WEST, door);
		Square square5 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders2);
		square4.controlBorder(square5, Direction.EAST);
		assertEquals(door1,square4.getBorder(Direction.EAST));
		assertEquals(door1,square5.getBorder(Direction.WEST));
	}
	@Test
	public void testControlBorderTwoWallConflict(){
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.EAST, wall2);
		Square square4 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		Border door = new Wall(true);
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.WEST, door);
		Square square5 = new OrdinarySquare(300,new BigDecimal(10), Scale.CELCIUS, borders2);
		square4.controlBorder(square5, Direction.EAST);
		assertEquals(wall2,square4.getBorder(Direction.EAST));
		assertEquals(wall2,square5.getBorder(Direction.WEST));
	}
	@Test
	public void testControlBorderWith2Rocks(){
		Square rock= new Rock();
		Square rock2=new Rock();
		rock.controlBorder(rock2, Direction.NORTH);
		assertEquals(rock.getBorder(Direction.NORTH),rock2.getBorder(Direction.SOUTH));
	}
	@Test
	public void testTerminate(){
		Border testWall1=new Wall(false);
		square.addBorder(Direction.CEILING, testWall1);
		square.terminate();
		assertFalse(testWall1.containsSquare(square));
	}
	
	@Test(expected = IllegalBorderException.class)
	public void testBorderRemoveWallTest(){
		square2.removeBorder(wall1);
	}
	@Test
	public void canHaveBorderRockTest(){
		Square square4 = new Rock();
		assertTrue(square4.getTemperature()==0);
		assertTrue(square4.getHumidity().floatValue()==Float.valueOf(0));
	}
	@Test(expected=IllegalBorderException.class)
	public void removeBorderFromRockTest(){
		(new Rock()).removeBorder(Direction.NORTH);
	}
	@Test(expected = IllegalBorderException.class)
	public void doorsNotOnOpposingSidesTransparentSquareTest(){
		Map<Direction, Border> borders=new HashMap<Direction,Border>();
		borders.put(Direction.NORTH, new Door());
		borders.put(Direction.EAST, new Door());
		new TransparentSquare(5,new BigDecimal(3),Scale.CELCIUS,borders);
	}
	@Test(expected=IllegalBorderException.class)
	public void threeDoorsTransparentSquareTest(){
		Square transparent=new TransparentSquare();
		transparent.addBorder(Direction.SOUTH,new Door());
		transparent.addBorder(Direction.CEILING,new Door());
	}
	@Test(expected=IllegalBorderException.class)
	public void constructTransparentSquareWithWallTest(){
		Map<Direction, Border> borders=new HashMap<Direction,Border>();
		borders.put(Direction.NORTH, new Wall(false));
		new TransparentSquare(0,new BigDecimal(0),Scale.CELCIUS,borders);
	}
	@Test (expected = IllegalArgumentException.class)
	public void removeNonExistingBorder(){
		Square square= new OrdinarySquare();
		square.addBorder(Direction.CEILING, new Wall(false));
		square.removeBorder(Direction.EAST);
	}
	@Test
	public void navigateSameAreaSameDungeon(){
		OrdinarySquare square1 = new OrdinarySquare();
		OrdinarySquare square2= new OrdinarySquare();
		Level<Square> level = new Level<Square>(new Position(50,50,0));
		level.addSquare(new Position(1,0,0), square1);
		level.addSquare(new Position(2,0,0), square2);
		square1.canNavigateTo(square2);
	}
	@Test
	public void navigateToSameAreaDifferentDungeon(){
		OrdinarySquare square1 = new OrdinarySquare();
		OrdinarySquare square2= new OrdinarySquare();
		CompositeDungeon<Square> compositeDungeon=new CompositeDungeon<Square>(new Position(50,50,50));
		Level<Square> level1 = new Level<Square>(new Position(2,2,0));
		Level<Square> level2 = new Level<Square>(new Position(2,2,0));
		level1.addSquare(new Position(2,1,0), square1);
		level2.addSquare(new Position(0,1,0), square2);
		compositeDungeon.addSubDungeon(level1, new Position(0,0,0));
		compositeDungeon.addSubDungeon(level2, new Position(3,0,0));
		assertTrue(square1.canNavigateTo(square2));
		
	}
	@Test
	public void navigateToThroughTeleportNotSameAreaSameDungeon(){
		OrdinarySquare square1 = new OrdinarySquare();
		OrdinarySquare square2= new OrdinarySquare();
		Level<Square> level = new Level<Square>(new Position(50,50,0));
		level.addSquare(new Position(1,0,0), square1);
		level.addSquare(new Position(3,2,0), square2);
		assertFalse(square1.canNavigateTo(square2));
		square1.addTeleportationDestination(square2);
		assertFalse(square2.canNavigateTo(square1));
		assertTrue(square1.canNavigateTo(square2));
		
	}
	@Test
	public void navigateToTeleportationDestinationLoopTest(){
		OrdinarySquare square = new OrdinarySquare();
		OrdinarySquare square2= new OrdinarySquare();
		OrdinarySquare square3=new OrdinarySquare();
		OrdinarySquare squareOtherDirection= new OrdinarySquare();
		square.addTeleportationDestination(square2);
		square.addTeleportationDestination(squareOtherDirection);
		squareOtherDirection.addTeleportationDestination(square);
		square2.addTeleportationDestination(square);
		square2.addTeleportationDestination(new OrdinarySquare());
		square2.addTeleportationDestination(square3);
		assertTrue(square.canNavigateTo(square3));
	}
	@Test
	public void navigateToTwoWayTeleportationDestinationFalse(){
		OrdinarySquare square = new OrdinarySquare();
		OrdinarySquare square2= new OrdinarySquare();
		OrdinarySquare square3=new OrdinarySquare();
		OrdinarySquare squareOtherDirection= new OrdinarySquare();
		square.addTeleportationDestination(square2);
		square.addTeleportationDestination(squareOtherDirection);
		squareOtherDirection.addTeleportationDestination(square);
		square2.addTeleportationDestination(square);
		square2.addTeleportationDestination(new OrdinarySquare());
		square2.addTeleportationDestination(square3);
		assertFalse(square.canNavigateTo(new OrdinarySquare()));
	}

	@Test
	public void areaTestWithSeveralCompositeDungeons(){
		CompositeDungeon<Square> dungeon1 = new CompositeDungeon<Square>(maxPosition);
		Level<Square> dungeon2 = new Level<Square>(new Position(2,2,0));
		dungeon1.addSubDungeon(dungeon2, new Position(0,0,0));
		CompositeDungeon<Square> dungeon4=new CompositeDungeon<Square>(new Position(50,50,0));
		Level<Square> dungeon3= new Level<Square>(new Position(2,2,0));
		dungeon4.addSubDungeon(dungeon3, new Position(0,0,0));
		dungeon1.addSubDungeon(dungeon4, new Position(0,3,0));
		Map<Direction,Border> borders= new HashMap<Direction,Border>();
		borders.put(Direction.CEILING,new Wall(false));
		Square square3=new OrdinarySquare(40, new BigDecimal(0), Scale.CELCIUS,borders);
		Square square1 = new OrdinarySquare();
		Square square2=new OrdinarySquare();
		Square square4=new OrdinarySquare();
		dungeon2.addSquare(new Position(1,2,0), square1);
		dungeon2.addSquare(new Position(2,2,0), square2);
		dungeon3.addSquare(new Position(1,0,0), square3);
		dungeon3.addSquare(new Position(2,0,0), square4);
		assertTrue(square2.canNavigateTo(square3));
	}
	@Test
	public void areaIsInSameAreaDoorCloseTest(){
		Map<Direction,Border>borders5 = new HashMap<Direction, Border>();
		Border wall = new Wall(false);
		borders5.put(Direction.FLOOR,wall);
		Square square3 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders5);
		Map<Direction,Border>borders3 = new HashMap<Direction, Border>();
		Border door1=new Door();
		borders3.put(Direction.NORTH,door1);
		Square square5= new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders3);
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		Border wall5 = new Wall(false);
		borders2.put(Direction.CEILING,wall5);
		Square square4 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders2);
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square3);
		dungeon.addSquare(position1, square4);
		dungeon.addSquare(position2, square5);
		square5.getBorder(Direction.NORTH).openOrClose();
		assertTrue(square5.canNavigateTo(square3));
		square5.getBorder(Direction.NORTH).openOrClose();
		assertFalse(square5.canNavigateTo(square3));
		
	}
	@Test
	public void areaTestAfterRemovalMiddle(){
		Border wall3=new Wall(false);
		Border wall4 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.FLOOR,wall3);
		borders1.put(Direction.EAST, wall4);
		Square square2 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders1);
		Map<Direction,Border>borders5 = new HashMap<Direction, Border>();
		Border wall = new Wall(false);
		borders5.put(Direction.FLOOR,wall);
		Square square3 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders5);
		Border wall5 = new Wall(false);
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.CEILING,wall5);
		Square square4 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders2);
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		dungeon.removeSquare(position1);
		assertFalse(square2.canNavigateTo(square4));
	}
	@Test
	public void areaTestAfterAddMiddle(){
		Border wall3=new Wall(false);
		Border wall4 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.FLOOR,wall3);
		borders1.put(Direction.EAST, wall4);
		Square square2 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders1);
		Map<Direction,Border>borders5 = new HashMap<Direction, Border>();
		Border wall = new Wall(false);
		borders5.put(Direction.FLOOR,wall);
		Square square3 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders5);
		Border wall5 = new Wall(false);
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.CEILING,wall5);
		Square square4 = new OrdinarySquare(100,new BigDecimal(10), Scale.CELCIUS, borders2);
		Position position=new Position(1,2,0);
		Position position1=new Position(1,3,0);
		Position position2=new Position(1,4,0);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position2, square4);
		dungeon.addSquare(position1, square3);
		assertTrue(square2.canNavigateTo(square4));
	}
	@Test
	public void navigateToRockTest(){
		OrdinarySquare square1 = new OrdinarySquare();
		Rock square2= new Rock();
		Level<Square> level = new Level<Square>(new Position(50,50,0));
		level.addSquare(new Position(1,0,0), square1);
		level.addSquare(new Position(2,0,0), square2);
		assertFalse(square1.canNavigateTo(square2));
	}
	@Test
	public void navigateToThroughDoorToOtherDungeonTest(){
		Level<Square> level1= new Level<Square>(new Position(1,1,0));
		Level<Square> level2= new Level<Square>(new Position(1,1,0));
		CompositeDungeon<Square> dungeon= new CompositeDungeon<Square>(new Position(5,5,5));
		dungeon.addSubDungeon(level1, new Position(0,0,0));
		dungeon.addSubDungeon(level2, new Position(2,0,0));
		Square squareWithDoor=new OrdinarySquare();
		Square other = new OrdinarySquare();
		squareWithDoor.addBorder(Direction.EAST,new Door());
		level1.addSquare(new Position(1,1,0),squareWithDoor );
		level2.addSquare(new Position(0,1,0), other);
		assertFalse(squareWithDoor.canNavigateTo(other));
		squareWithDoor.getBorder(Direction.EAST).openOrClose();
		assertTrue(squareWithDoor.canNavigateTo(other));
	}
	@Test
	public void canNavigateToNotSameArea(){
		Level<Square> level1= new Level<Square>(new Position(3,3,0));
		Square one = new OrdinarySquare();
		Square two = new OrdinarySquare();
		level1.addSquare(new Position(1,0,0), one);
		level1.addSquare(new Position(2,1,0), two);
		assertFalse(one.canNavigateTo(two));
	}

		
}
